package com.dex4d.ui.dapp.model;

import android.app.Activity;

import com.dex4d.constants.IConstants;
import com.dex4d.core.http.DefaultTransformer;
import com.dex4d.core.http.JsonApiWrapper;
import com.dex4d.core.subscribers.ProgressSubscriber;
import com.dex4d.model.HttpResult;
import com.dex4d.model.RequestParam;
import com.dex4d.model.User;
import com.dex4d.ui.main.model.Dex4dTransaction;
import com.dex4d.utils.CryptoUtils;
import com.dex4d.utils.EthWalletUtil;
import com.dex4d.utils.Log;
import com.dex4d.utils.SPUtil;
import com.wallet.web3.entity.Web3Transaction;

import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import rx.Observable;
import rx.functions.Func1;

/**
 * 作者    LiQingyi
 * 时间    2018/12/4 11:41
 * 文件    dex4d-android
 * 描述    Dapp 浏览器转账交易
 */
public class DappTrade {

    private Activity activity;
    private BigInteger nonce;

    private Web3Transaction transaction;

    private OnTransferListener transferListener;

    public DappTrade(Activity activity, OnTransferListener transferListener) {
        this.activity = activity;
        this.transferListener = transferListener;
        this.nonce = BigInteger.ZERO;
    }

    public DappTrade(Activity activity) {
        this.activity = activity;
        this.nonce = BigInteger.ZERO;
    }

    public void setTransaction(Web3Transaction transaction) {
        this.transaction = transaction;
    }

    /**
     * 交易
     */
    public void trade() {

        Observable
                .zip(nonce(),
                        gasPrice(),
                        (n, g) -> {
                            Dex4dTransaction dex4dTransaction =
                                    new Dex4dTransaction(transaction.gasLimit);
                            dex4dTransaction.setNonce(n.getResult());
                            nonce = dex4dTransaction.nonce;
                            dex4dTransaction.setGasPrice(g.getResult());
                            return dex4dTransaction;
                        })
                .map(dex4dTransaction -> {
                    RawTransaction rawTransaction = getRawTransaction(
                            dex4dTransaction.nonce,
                            transaction.recipient.toString(),
                            dex4dTransaction.gasPrice,
                            transaction.gasLimit,
                            transaction.value,
                            transaction.payload);

                    Log.i("AAA" + ":" + rawTransaction.getNonce().toString());
                    Log.i("AAA" + ":" + rawTransaction.getTo());
                    Log.i("AAA" + ":" + rawTransaction.getValue().toString());
                    Log.i("AAA" + ":" + rawTransaction.getData());

                    String password = User.getInstance().getPassphrase(); //解析密码
                    Credentials credentials = loadCredentials(password);
                    byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
                    String signData = Numeric.toHexString(signedMessage);
                    return signData;
                })
                .flatMap((Func1<String, Observable<HttpResult<String>>>) sign -> send(sign))
                .map(result -> result.getResult())
                .compose(new DefaultTransformer<String>())
                .subscribe(new ProgressSubscriber<String>(activity, true) {
                    @Override
                    public void onNext(String result) {
                        transferListener.success(transaction, result);
                    }

                    @Override
                    public void onError(Throwable e) {
                        super.onError(e);
                        transferListener.error(transaction, e.getMessage());
                    }
                });
    }

    /**
     * 加载证书
     *
     * @param password
     * @return
     */
    private Credentials loadCredentials(String password) {
        String keystore = User.getInstance().getKeystore();
        String pwd = password + EthWalletUtil.getSignature().substring(0, 18);
        try {
            keystore = CryptoUtils.decrypt(keystore, pwd); //解密
        } catch (Exception e) {
            e.printStackTrace();
        }
        String priKey = EthWalletUtil.getPriKey(keystore, password);
        return Credentials.create(priKey);
    }

    /**
     * 获取Nonce
     *
     * @return
     */
    private Observable<HttpResult<String>> nonce() {
        List<String> address = Collections.singletonList(User.getInstance().getAddress());
        final RequestParam nonceParam = new RequestParam();
        nonceParam.setId(1);
        nonceParam.setJsonrpc(IConstants.JSON_RPC_VERSION);
        nonceParam.setMethod(IConstants.CONTRACTSERVICE_GETNONCE);
        nonceParam.setParams(address);
        return JsonApiWrapper
                .serviceApi()
                .requestByParam(nonceParam);
    }


    /**
     * 获取Gas价格
     *
     * @return
     */
    private Observable<HttpResult<String>> gasPrice() {
        List<String> address = Collections.singletonList(User.getInstance().getAddress());
        final RequestParam gasParam = new RequestParam();
        gasParam.setId(2);
        gasParam.setJsonrpc(IConstants.JSON_RPC_VERSION);
        gasParam.setMethod(IConstants.CONTRACTSERVICE_GASPRICE);
        gasParam.setParams(address);
        return JsonApiWrapper
                .serviceApi()
                .requestByParam(gasParam);
    }

    private RawTransaction getRawTransaction(BigInteger nonce,
                                             String to,
                                             BigInteger gasPrice,
                                             BigInteger gasLimit,
                                             BigInteger value,
                                             String data) {
        return RawTransaction.createTransaction(
                nonce,
                gasPrice,
                gasLimit,
                to,
                value,
                data);
    }

    /**
     * 签名交易
     *
     * @param sign
     * @return
     */
    private Observable<HttpResult<String>> send(String sign) {
        final RequestParam txParam = new RequestParam();
        txParam.setId(3);
        txParam.setJsonrpc(IConstants.JSON_RPC_VERSION);
        txParam.setMethod(IConstants.CONTRACTSERVICE_SENDRAWTRANSACTION);
        txParam.setParams(Collections.singletonList(sign));
        return JsonApiWrapper
                .serviceApi()
                .requestByParam(txParam)
                .map(stringHttpResult -> {
                    //本地存储增加nonce
                    SPUtil.increaseNonce(nonce);
                    return stringHttpResult;
                });
    }

    /**
     * 获取消耗Gas费用
     */
    public void estimateGas(String data, String to, BigInteger value) {
        final RequestParam param = new RequestParam();
        param.setId(2);
        param.setJsonrpc(IConstants.JSON_RPC_VERSION);
        param.setMethod(IConstants.ESTIMATE_GAS);
        param.setParams(Arrays.asList(data, to, value.toString()));

        JsonApiWrapper
                .serviceApi()
                .requestByParam(param)
                .compose(new DefaultTransformer<HttpResult<String>>())
                .subscribe(new ProgressSubscriber<HttpResult<String>>(activity, true) {
                    @Override
                    public void onNext(HttpResult<String> result) {

                    }

                    @Override
                    public void onError(Throwable e) {
                        super.onError(e);
                    }
                });

    }

    public interface OnTransferListener {
        public void success(Web3Transaction transaction, String result);

        public void error(Web3Transaction transaction, String result);
    }

}
